package entity

import (
	"fmt"
	"src/gcommon"
	"src/zeus/dbservice"
	"src/zeus/iserver"
	"src/zeus/logger"
	"src/zeus/msgdef"
)

func (e *Entity) isEntityExisted(srvType uint8) bool {
	//如果我自己是带场景的服务器，那么我就没有连接其他场景的链接；不然就会给自己发消息
	if srvType == iserver.ServerTypeSpace {
		if e.spaceID != 0 {
			return false
		}
	}
	e.Mux.RLock()
	defer e.Mux.RUnlock()
	for i := range e.srvIDS {
		if srvType == iserver.ServerTypeSpace {
			//如果是要去场景服务器，就判断哪个服务器有SpaceID
			if e.srvIDS[i].SpaceID != 0 {
				return true
			}
		} else if e.srvIDS[i].Stype == srvType {
			return true
		}
	}
	return false
	// _, ok := e.srvIDS[srvType]

	// return ok
}

func (e *Entity) getEntitySrvByStype(srvType uint8) (result *dbservice.EntitySrvInfo, ok bool) {
	e.Mux.RLock()
	defer e.Mux.RUnlock()
	for i := range e.srvIDS {
		if srvType == iserver.ServerTypeSpace {
			//如果是要去场景服务器，就判断哪个服务器有SpaceID
			if e.srvIDS[i].SpaceID != 0 {
				return e.srvIDS[i], true
			}
		} else if e.srvIDS[i].Stype == srvType {
			return e.srvIDS[i], true
		}
	}
	return nil, false
}

func (e *Entity) GetEntitySrvID(srvType uint8) (uint64, uint64, error) {
	srvmd, ok := e.getEntitySrvByStype(srvType)
	if ok {
		return srvmd.SrvID, srvmd.SpaceID, nil
	}
	// 第一次尝试不成功, 则先刷新一次信息
	e.RefreshSrvIDS()

	srvmd, ok = e.getEntitySrvByStype(srvType)
	if !ok {
		return 0, 0, fmt.Errorf("Entity srvType [%d] not existed", srvType)
	}
	return srvmd.SrvID, srvmd.SpaceID, nil
}

// 本对象有没有连接指点的服务器
func (e *Entity) IsExistSID(srvid uint64) bool {
	e.Mux.RLock()
	defer e.Mux.RUnlock()
	e.Debugf("IsEntityExisted eid = %d, srvid = %d ? Check SrvIds = %v", e.GetID(), srvid, gcommon.ToJsonString(e.srvIDS))
	for i := range e.srvIDS {
		if e.srvIDS[i].SrvID == srvid {
			return true
		}
	}
	return false
}

// GetSrvIDS 获取玩家的分布式实体所在的服务器列表
func (e *Entity) GetSrvIDS() (result []*dbservice.EntitySrvInfo) {
	e.Mux.RLock()
	defer e.Mux.RUnlock()
	result = make([]*dbservice.EntitySrvInfo, len(e.srvIDS))
	copy(result, e.srvIDS)
	return result
}

// RefreshSrvIDS 从redis上更新 当前 entity所有的分布式信息
func (e *Entity) RefreshSrvIDS() {
	if e.flyEntity {
		return
	}

	e.Mux.Lock()
	defer e.Mux.Unlock()

	srvIDs, err := dbservice.EntitySrvUtil(e.entityID).GetSrvIDs()
	if err != nil {
		logger.Error("Get entity srv info failed", err)
		return
	}

	e.srvIDS = e.srvIDS[0:0]
	// e.spaceID = 0
	if e.spaceID != 0 {
		e.spaceSrvType = iserver.GetSrvInst().GetCurSrvInfo().Type
	} else {
		e.spaceSrvType = 0
	}
	/*
		这里从REDIS中拿到ntt对象的分布信息
		先是要把自己的信息写入
		然后是要把自己没有连接的信息去掉
	*/
	for _, info := range srvIDs {
		if iserver.GetSrvInst().GetCurSrvInfo().Type == info.Stype {
			//logger.Info("RefreshSrvIDS eid = %d, sid = %d", e.GetID(), info.SpaceID)
			// if info.SpaceID != 0 {
			// 	e.spaceID = info.SpaceID
			// 	e.spaceSrvType = info.Stype
			// }
		} else if !iserver.GetSrvInst().IsSessExist(info.SrvID) {
			continue
		}
		e.srvIDS = append(e.srvIDS, info)
	}
}

// regSrvID 将当前部分的Entity注册到Redis上
func (e *Entity) regSrvID() {
	/*
		注册如果失败，应该走卸载逻辑
		主要注册失败应该是场景部分的逻辑
		场景注册逻辑修改为SaveType，
		这样大场景与副本就一样了
	*/
	if err := dbservice.EntitySrvUtil(e.entityID).RegSrvID(
		iserver.GetSrvInst().GetSaveType(),
		iserver.GetSrvInst().GetSrvID(),
		e.GetSpaceID(),
		e.entityType,
		e.dbid); err != nil {
		logger.Warnf("Reg SrvID failed %s", err)
		e.isFailLoad = true
		e.entitiesPtr.CallDelay(func() {
			logger.Warnf("Reg SrvID failed DestroyEntity")
			e.entitiesPtr.DestroyEntity(e.GetID())
		})
		return
	}

	if iserver.GetSrvInst().GetSrvType() == iserver.ServerTypeGateway {
		if err := dbservice.EntitySrvUtil(e.entityID).RegSrvID(
			iserver.ServerTypeClient,
			iserver.GetSrvInst().GetSrvID(),
			e.GetSpaceID(),
			e.entityType,
			e.dbid); err != nil {
			logger.Warnf("Reg SrvID failed %s", err)
			e.isFailLoad = true
			e.entitiesPtr.CallDelay(func() {
				e.entitiesPtr.DestroyEntity(e.GetID())
			})
			return
		}
	}
	e.RefreshSrvIDS()
	e.boardCastSrvInfo()
}

// unregSrvID 将当前部分的Entity从Redis上删除
func (e *Entity) unregSrvID() {
	if err := dbservice.EntitySrvUtil(e.entityID).UnRegSrvID(
		iserver.GetSrvInst().GetSaveType(),
		iserver.GetSrvInst().GetSrvID(),
		e.GetSpaceID()); err != nil {
		logger.Warnf("Unreg SrvID failed %s", err)
	}

	if iserver.GetSrvInst().GetSrvType() == iserver.ServerTypeGateway {
		if err := dbservice.EntitySrvUtil(e.entityID).DelEntitySrvInfo(); err != nil {
			logger.Warnf("Unreg SrvID failed %s", err)
		}
	}
	e.boardCastSrvInfo()
}

func (e *Entity) boardCastSrvInfo() {

	srvli := e.GetSrvIDS()
	for _, srvmd := range srvli {
		if srvmd.Stype != iserver.ServerTypeClient && srvmd.Stype != iserver.GetSrvInst().GetSrvType() {
			e.Post(srvmd.Stype, &msgdef.EntitySrvInfoNotify{})
		}
	}
}

func (e *Entity) MsgProc_EntitySrvInfoNotify(msgdef.IMsg) {
	e.RefreshSrvIDS()
}
